package com.github.sbb.sherry.primitives.fetcher.mysql;

import com.github.sbb.sherry.common.base.Preconditions;
import com.github.sbb.sherry.common.base.ValidateResult;
import com.github.sbb.sherry.primitives.config.datasource.DataSource;
import com.github.sbb.sherry.primitives.config.datasource.DataSourceQueryException;
import com.github.sbb.sherry.primitives.config.datasource.MySQLDataSourceConfig;
import com.github.sbb.sherry.primitives.config.fetcher.ColumnItem;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MySQLDataSource implements DataSource {

  /**
   * druid 连接池
   */
  private HikariDataSource hikariDataSource;

  public MySQLDataSource(MySQLDataSourceConfig mySQLDataSourceConfig) {

    Preconditions.checkNotNull(mySQLDataSourceConfig, "mySQLDataSourceConfig can`t be null");
    ValidateResult validate = mySQLDataSourceConfig.validate();
    Preconditions.checkArgument(validate.isPass(), validate.getMessage());

    HikariConfig config = new HikariConfig();

    String jdbcUrl = String.format("jdbc:mysql://%s:%s/%s?useUnicode=true&characterEncoding=utf8&autoReconnect=true",
        mySQLDataSourceConfig.getHost(), mySQLDataSourceConfig.getPort(), mySQLDataSourceConfig.getDatabase());

    config.setJdbcUrl(jdbcUrl);
    config.setUsername(mySQLDataSourceConfig.getUsername());
    config.setPassword(mySQLDataSourceConfig.getPassword());
    config.setMinimumIdle(mySQLDataSourceConfig.getMinimumIdle());
    config.setMaximumPoolSize(mySQLDataSourceConfig.getMaximumPoolSize());
    config.setConnectionTestQuery("select 1");
    config.validate();

    hikariDataSource = new HikariDataSource(config);
  }

  public Map<String, Object> selectOne(String table, List<ColumnItem> fields, String conditions)
      throws DataSourceQueryException {

    try (Connection connection = hikariDataSource.getConnection()) {
      Map<String, Object> result;
      try (Statement stmt = connection.createStatement()) {
        ResultSet rs = stmt.executeQuery(buildSql(table, fields, conditions, 1));
        result = new HashMap<>();
        if (rs.next()) {
          for (ColumnItem column : fields) {
            result.put(column.getAlias(), rs.getObject(column.getAlias()));
          }
        }
        rs.close();
      }
      return result;
    } catch (Exception e) {
      throw new DataSourceQueryException(e);
    }
  }

  public List<Map<String, Object>> selectMany(String table, List<ColumnItem> fields, String conditions, int limit)
      throws DataSourceQueryException {

    try (Connection connection = hikariDataSource.getConnection()) {
      List<Map<String, Object>> result;
      try (Statement stmt = connection.createStatement()) {
        ResultSet rs = stmt.executeQuery(buildSql(table, fields, conditions, limit));
        result = new ArrayList<>();
        while (rs.next()) {
          Map<String, Object> dataset = new HashMap<>();
          for (ColumnItem column : fields) {
            dataset.put(column.getAlias(), rs.getObject(column.getAlias()));
          }
          result.add(dataset);
        }
        rs.close();
      }
      return result;
    } catch (Exception e) {
      throw new DataSourceQueryException(e);
    }
  }

  @Override
  public void check() {
    try (Connection connection = hikariDataSource.getConnection();) {
      connection.getMetaData();
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public void close() {
    if (hikariDataSource != null) {
      hikariDataSource.close();
      hikariDataSource = null;
    }
  }

  private String buildSql(String table, List<ColumnItem> columnsInfo, String conditions, int limit) {
    StringBuilder sb = new StringBuilder();

    List<String> columns = new ArrayList<>();
    for (ColumnItem field : columnsInfo) {
      columns.add("`" + field.getColumn() + "` as `" + field.getAlias() + "`");
    }
    String column = String.join(",", columns);

    sb.append("select ").append(column)
        .append(" from ").append(table)
        .append(" where ").append(conditions);

    if (limit < 1) {
      sb.append(";");
    } else {
      sb.append(" limit ").append(limit).append(";");
    }

    return sb.toString();
  }
}
